/*
 * VoltageTraceDigitizer.cc
 *
 * @created Apr 19, 2013
 * @author Tim Niggemann, III Phys. Inst. A, RWTH Aachen University
 * @copyright GNU General Public License v3.0
 */

#include "digi/G4SipmVoltageTraceDigitizer.hh"

#include <G4DigiManager.hh>
#include <G4Timer.hh>

#include <CLHEP/Random/RandGauss.h>

#include "G4SipmUiMessenger.hh"

G4SipmVoltageTraceDigitizer::G4SipmVoltageTraceDigitizer(G4Sipm *_sipm) : G4VDigitizerModule(_sipm->getVoltageTraceDigitizerName()), sipm(_sipm)
{
	collectionName.push_back(sipm->getVoltageTraceDigiCollectionName());
	// Register to Geant4.
	G4DigiManager::GetDMpointer()->AddNewModule(this);
}

G4SipmVoltageTraceDigitizer::~G4SipmVoltageTraceDigitizer()
{
	//
}

const G4SipmDigiCollection *G4SipmVoltageTraceDigitizer::getDigiCollection()
{
	G4DigiManager *digiManager = G4DigiManager::GetDMpointer();
	G4int collId = digiManager->GetDigiCollectionID(sipm->getDigiCollectionName());
	if (collId >= 0)
	{
		const G4SipmDigiCollection *digiCollection =
			static_cast<const G4SipmDigiCollection *>(digiManager->GetDigiCollection(collId));
		return digiCollection;
	}
	return NULL;
}
// 模拟SiPM的电压波形，完成电压波形数字化对象的创建、脉冲波形的叠加、以及基线噪声和白噪声的添加。
void G4SipmVoltageTraceDigitizer::Digitize()
{
	if (!sipm || !G4SipmUiMessenger::getInstance()->isDigitizeTrace())
	{
		return;
	}
	// 开始计时
	G4Timer timer;
	timer.Start();
	// Get Digi collection.
	// 获取数字化集合
	const G4SipmDigiCollection *digis = getDigiCollection();
	if (!digis || digis->GetSize() == 0)
	{
		return;
	}
	// 获取电压波形模型
	G4SipmVoltageTraceModel *voltageTraceModel = sipm->getModel()->getVoltageTraceModel();
	// Assuming digis are ordered chronologically, get the first and last.
	// 确定时间范围，即波形的时间范围，从第一个光电二极管响应开始到最后一个光电二极管响应结束之后10*tauFall时间。
	double tMin = reinterpret_cast<G4SipmDigi *>(digis->GetVector()->front())->getTime();
	double tMax = reinterpret_cast<G4SipmDigi *>(digis->GetVector()->back())->getTime() + 10. * voltageTraceModel->getTauFall();
	// Create trace.
	// 创建电压波形数字化对象，其中包括光电二极管响应脉冲波形和基线噪声。
	G4SipmVoltageTraceDigi *trace = new G4SipmVoltageTraceDigi(sipm->getId(), tMin, tMax,
															   voltageTraceModel->getTimeBinWidth());
	trace->setPrecision(voltageTraceModel->getPrecision());
	// 遍历每个数字化点，并将其脉冲波形叠加到电压波形数字化对象上。
	for (size_t i = 0; i < digis->GetSize(); i++)
	{
		G4SipmDigi *d = reinterpret_cast<G4SipmDigi *>(digis->GetDigi(i));
		// Add trigger to trace.
		// 叠加10*tauFall时间内的光电二极管响应脉冲波形。
		double ti = 0.;
		while (ti < 10. * voltageTraceModel->getTauFall())
		{
			// 计算光电二极管响应脉冲波形的时间索引。
			size_t index = trace->index(ti + d->getTime());
			if (index < trace->size())
			{
				// 计算光电二极管响应脉冲波形的高度
				double vi = voltageTraceModel->pulse(ti, d->getWeight());
				// If pulse gets really small break early.
				// 脉冲波形很小的时候，停止叠加。
				if (ti > voltageTraceModel->getTauRise() && vi < 1e-4 * voltageTraceModel->getAmplitude())
				{
					break;
				}
				// 叠加光电二极管响应脉冲波形到电压波形数字化对象上。
				trace->at(index) += vi;
			}
			// 时间索引增加一个时间间隔。
			ti += voltageTraceModel->getTimeBinWidth();
		}
	}
	// Add baseline and white noise.
	// 添加基线噪声和白噪声。
	std::vector<double> avg(4, 0);
	for (size_t i = 0; i < trace->size(); i++)
	{
		// Compute 3 point moving average of white noise.
		// 三点均值滤波，用于平滑噪声。
		avg[i % avg.size()] = CLHEP::RandGauss::shoot(0., voltageTraceModel->getWhiteNoiseSigma());
		trace->at(i) += voltageTraceModel->getV0() + (avg[0] + avg[1] + avg[2]) / static_cast<double>(avg.size());
	}
	// Store trace.
	// 储存电压波形数据
	G4SipmVoltageTraceDigiCollection *traces = new G4SipmVoltageTraceDigiCollection(GetName(), GetCollectionName(0));
	traces->insert(trace);
	StoreDigiCollection(traces);
	// Stop timer and print information.
	// 停止计时并打印信息。
	timer.Stop();
	std::cout << "G4SipmVoltageTraceDigitizer::Digitize(): added digi collection \"" << traces->GetName()
			  << "\" for digitizer \"" << GetName() << "\" with " << traces->GetSize() << " entries " << "in (" << tMin
			  << ", " << tMax << ") ns " << "(" << timer << ")." << std::endl;
}

G4Sipm *G4SipmVoltageTraceDigitizer::getSipm() const
{
	return sipm;
}

void G4SipmVoltageTraceDigitizer::setSipm(G4Sipm *_sipm)
{
	sipm = _sipm;
}
